package top.eggcode.plugins.office;

import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.AcroFields;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfStamper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import top.eggcode.common.error.ApplicationException;
import top.eggcode.common.file.FileCompressUtil;
import top.eggcode.common.model.UidGenerator;
import top.eggcode.plugins.office.common.DocumentCategory;
import top.eggcode.plugins.office.common.PathGenerator;
import top.eggcode.plugins.office.func.ExcelRenderFunction;
import top.eggcode.plugins.office.func.PdfRenderFunction;
import top.eggcode.plugins.office.func.WordRenderFunction;

import java.io.*;
import java.util.Map;
import java.util.Objects;

/**
 * 文档导出工具
 * 使用外观模式重构
 * Created at 2020/12/3 9:21。
 * 说明：{
 * PDF模板建议使用万兴PDF大师及Adobe Acrobat软件编辑；
 * 本工具在运行时会消耗大量CPU资料，请与重要任务隔离；
 * 本工具类在运行时会产生临时文件，在文档制作完后，调用 cleanTemporaryFile 方法可以清理掉所有文件。当然你也可以通过定时任务解决这个问题；
 * }
 *
 * @author DingJiaQi
 * @version 1.0
 */
@Slf4j
public class DocumentFacade {

    private final String tempDir;

    private final PathGenerator pathGenerator;

    private final PdfRender pdfReader;

    private final WordRender wordRender;

    public DocumentFacade(String appTempDir) {
        tempDir = appTempDir + "/" + UidGenerator.apply() + "/";
        this.pathGenerator = new PathGenerator(tempDir);

        this.pdfReader = new PdfRender(pathGenerator);

        this.wordRender = new WordRender(pathGenerator);
    }

    /**
     * 清理临时文件
     */
    public void cleanTemp() {
        FileCompressUtil.delDirRecursively(new File(tempDir));
    }


    /**
     * 生成Excel文档
     * 说明：{
     * XSSFWorkbook支持 .xls 或 .xlsx 文件
     * }
     *
     * @param templatePath 模板路径
     * @param documentPath 文档保存路径
     * @param props        《单元格地址，数据》集合
     * @throws IOException 文件读取异常
     */
    public void makeExcel(String templatePath,
                          String documentPath,
                          Map<String, String> props,
                          ExcelRenderFunction action) throws IOException {
        InputStream in = new FileInputStream(templatePath);
        OutputStream out = new FileOutputStream(documentPath);
        XSSFWorkbook workbook = new XSSFWorkbook(in);
        // 渲染
        action.render(workbook, props);
        // 保存
        workbook.write(out);
        // 收尾
        in.close();
        out.close();
        workbook.close();
    }

    /**
     * 生成Excel文档
     * 说明：{
     * XSSFWorkbook支持 .xls 或 .xlsx 文件
     * }
     *
     * @param templateIn  模板路径
     * @param documentOut 文档保存路径
     * @param props       《单元格地址，数据》集合
     * @throws IOException 文件读取异常
     */
    public void makeExcel(InputStream templateIn,
                          OutputStream documentOut,
                          Map<String, String> props,
                          ExcelRenderFunction action) throws IOException {

        XSSFWorkbook workbook = new XSSFWorkbook(templateIn);
        // 渲染
        action.render(workbook, props);

        workbook.write(documentOut);
        workbook.close();
    }

    /**
     * 生成word文档
     *
     * @param templatePath 模板路径
     * @param savePath     保存路径
     * @param props        模板数据
     */
    public void makeWord(String templatePath,
                         String savePath,
                         Map<String, String> props) {
        try (
                // 创建与源文件无关联的文档对象
                XWPFDocument template = new XWPFDocument(new FileInputStream(templatePath));
                FileOutputStream documentOut = new FileOutputStream(savePath)
        ) {
            wordRender.toText(template, props);
            template.write(documentOut);
        } catch (IOException e) {
            throw new ApplicationException("文档制作失败！", e);
        }

    }

    /**
     * 生成word文档
     *
     * @param in    读取IO
     * @param out   输出IO
     * @param props 模板数据
     */
    public void makeWord(InputStream in,
                         OutputStream out,
                         Map<String, String> props) {
        String documentSavePath = this.pathGenerator.getDocument(DocumentCategory.WORD2007);
        try (
                // 创建与源文件无关联的文档对象
                XWPFDocument template = new XWPFDocument(in);
                FileOutputStream documentOut = new FileOutputStream(documentSavePath)
        ) {
            wordRender.toText(template, props);
            template.write(documentOut);
            // 将制作好的文档输出到 out 中
            moveFile(documentSavePath, out);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            FileCompressUtil.deleteFile(documentSavePath);
        }

    }

    /**
     * 生成word文档
     *
     * @param in    读取IO
     * @param out   输出IO
     * @param props 模板数据
     */
    public void makeWord(InputStream in,
                         OutputStream out,
                         Map<String, String> props,
                         WordRenderFunction action) {
        try (
                // 创建与源文件无关联的文档对象
                XWPFDocument document = new XWPFDocument(in)
        ) {
            action.render(document, props);
            document.write(out);
        } catch (IOException e) {
            e.printStackTrace();
        }


    }

    /**
     * 生成PDF
     * 说明：{
     * 如果功能无法满足你，你可以重写本方法。
     * }
     *
     * @param in    读取IO
     * @param out   输出IO
     * @param props 数据
     */
    public void makePdf(InputStream in,
                        OutputStream out,
                        Map<String, String> props,
                        PdfRenderFunction action) {
        String documentSavePath = this.pathGenerator.getDocument(DocumentCategory.WORD2007);
        PdfStamper formStamper = null;
        PdfReader templatePdf = null;
        FileOutputStream documentOut = null;
        try {

            documentOut = new FileOutputStream(documentSavePath);

            templatePdf = new PdfReader(in);
            // 3 生成模板的副本
            formStamper = new PdfStamper(templatePdf, documentOut);
            // 4 获取pdf表单
            AcroFields formField = formStamper.getAcroFields();
            // 设置字体
            BaseFont formFont = BaseFont.createFont("static/template/recruit/apply_student/simsun.ttf",
                    BaseFont.IDENTITY_H,
                    BaseFont.EMBEDDED);
            // 设置表单域字体
            formField.addSubstitutionFont(formFont);

            // 将表单域转换为普通文本
            formStamper.setFormFlattening(true);

            // 渲染模式，预录取通知书
            //patternPreAdmissionLetter(formStamper, formField, props);
            Objects.requireNonNull(action);
            action.render(formStamper, formField, props);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                Objects.requireNonNull(formStamper).close();
                Objects.requireNonNull(templatePdf).close();
                // 必须在 PdfStamper的实例 之后关闭
                Objects.requireNonNull(documentOut).close();
            } catch (DocumentException e) {
                log.error("模板制作异常");
                e.printStackTrace();
            } catch (IOException e) {
                log.error("文件保存异常");
                e.printStackTrace();
            }
        }
        // 将制作好的文档输出到 out 中
        moveFile(documentSavePath, out);

    }

    /**
     * 生成PDF
     * 说明：{
     * 如果功能无法满足你，你可以重写本方法。
     * }
     *
     * @param templatePath 模板路径
     * @param documentPath 成品输出路径
     * @param props        数据
     */
    public void makePdf(String templatePath,
                        String documentPath,
                        Map<String, String> props,
                        PdfRenderFunction action) {
        PdfStamper formStamper = null;
        PdfReader templatePdf = null;
        FileOutputStream documentOut = null;
        try {

            documentOut = new FileOutputStream(documentPath);

            templatePdf = new PdfReader(new FileInputStream(new File(templatePath)));
            // 3 生成模板的副本
            formStamper = new PdfStamper(templatePdf, documentOut);
            // 4 获取pdf表单
            AcroFields formField = formStamper.getAcroFields();
            // 设置字体
            BaseFont formFont = BaseFont.createFont("static/template/recruit/apply_student/simsun.ttf",
                    BaseFont.IDENTITY_H,
                    BaseFont.EMBEDDED);
            // 设置表单域字体
            formField.addSubstitutionFont(formFont);

            // 将表单域转换为普通文本
            formStamper.setFormFlattening(true);

            // 渲染模式，预录取通知书
            //patternPreAdmissionLetter(formStamper, formField, props);
            Objects.requireNonNull(action);
            action.render(formStamper, formField, props);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                Objects.requireNonNull(formStamper).close();
                Objects.requireNonNull(templatePdf).close();
                // 必须在 PdfStamper的实例 之后关闭
                Objects.requireNonNull(documentOut).close();
            } catch (DocumentException e) {
                log.error("模板制作异常");
                e.printStackTrace();
            } catch (IOException e) {
                log.error("文件保存异常");
                e.printStackTrace();
            }
        }
    }

    /**
     * 移动文件
     *
     * @param source 源文件位置
     * @param out    输出流
     */
    void moveFile(String source, OutputStream out) {
        if (StringUtils.isNotBlank(source) && out != null) {

            byte[] buffer = new byte[2048];
            try (InputStream in = new FileInputStream(source)) {
                // 数据长度记录
                int dataLength = 0;
                while ((dataLength = in.read(buffer)) != -1) {
                    out.write(buffer, 0, dataLength);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}
